package org.metasketch.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ReflectionAnalyser {

	public static RefUtil util = new RefUtil();

	public static void setObjectToCollectionsPerProperType(Object parent,
			Object child) throws Exception {

		Class parentType = parent.getClass();

		List<String> collections = getCollectionFieldNames(parentType);
		for (String collectionName : collections) {
			Class collectionType = util.isSpecificList(parentType,
					collectionName);

			if (collectionType.isAssignableFrom(child.getClass())) {
				util.setValue(parent, collectionName, child);
			}

		}

	}

	public static List<String> getCompatibleCollections(Class parentType,
			Class childType) throws Exception {

		List<String> compatibleCollections = new ArrayList<String>();

		List<String> collections = getCollectionFieldNames(parentType);
		for (String collectionName : collections) {
			Class collectionType = util.isSpecificList(parentType,
					collectionName);

			if (collectionType.isAssignableFrom(childType)) {
				compatibleCollections.add(collectionName);
			}

		}

		return compatibleCollections;
	}

	public static List<String> getBasicFieldNames(Class type) throws Exception {

		System.out.println("Basic fields for class:" + type.getCanonicalName());

		List<String> basicFields = new ArrayList<String>();

		List<String> fields = getJavaBeanFieldNames(type);

		for (String fieldName : fields) {
			Field field = util.getField(type, fieldName);
			Class<?> fieldType = field.getType();

			if (util.isPrimitiveOrProxyOrString(fieldType)) {
				basicFields.add(fieldName);
				System.out.println(fieldName);
			}

		}

		return basicFields;
	}

	public static List<String> getCollectionFieldNames(Class type)
			throws Exception {

		System.out.println("Collection fields for class:"
				+ type.getCanonicalName());

		List<String> collectionFields = new ArrayList<String>();

		List<String> fields = getJavaBeanFieldNames(type);

		for (String fieldName : fields) {
			Field field = util.getField(type, fieldName);
			// Class<?> fieldType = field.getType();

			Class listType = util.isSpecificList(field);

			if (listType != null) {
				collectionFields.add(fieldName);
				System.out.println("List<" + listType.getCanonicalName() + "> "
						+ fieldName);
			}

		}

		return collectionFields;
	}

	public static List<String> getObjectFieldNames(Class type) throws Exception {

		System.out
				.println("Object fields for class:" + type.getCanonicalName());

		List<String> objectFields = new ArrayList<String>();

		List<String> fields = getJavaBeanFieldNames(type);

		for (String fieldName : fields) {
			Field field = util.getField(type, fieldName);
			Class<?> fieldType = field.getType();

			if (!util.isPrimitiveOrProxyOrString(fieldType)
					&& util.isSpecificList(field) == null) {
				objectFields.add(fieldName);
				System.out.println(fieldName);
			}

		}

		return objectFields;
	}

	public static List<String> getNonNativeCollections(Class type)
			throws Exception {

		System.out.println("Non Native Collections for class:"
				+ type.getCanonicalName());

		List<String> objectFields = new ArrayList<String>();

		List<String> fields = getJavaBeanFieldNames(type);

		for (String fieldName : fields) {
			Field field = util.getField(type, fieldName);
			Class<?> fieldType = field.getType();

			if (!util.isPrimitiveOrProxyOrString(fieldType)
					&& util.isSpecificList(field) != null) {
				objectFields.add(fieldName);
				System.out.println(fieldName);
			}

		}

		return objectFields;
	}

	public static ClassFieldReport getClassFieldReport(Class type)
			throws Exception {

		System.out.println("Creating field report for class: "
				+ type.getCanonicalName());

		ClassFieldReport report = new ClassFieldReport();

		List<String> objectFields = new ArrayList<String>();

		List<String> fields = getJavaBeanFieldNames(type);

		for (String fieldName : fields) {
			Field field = util.getField(type, fieldName);
			Class<?> fieldType = field.getType();

			if (util.isSpecificList(field) != null) {
				System.out.println("List: " + fieldName);
				report.getCollectionFields().add(fieldName);
			} else if (util.isPrimitiveOrProxyOrString(fieldType)) {
				System.out.println("Basic: " + fieldName);
				report.getBasicFields().add(fieldName);
			} else {
				System.out.println("Object: " + fieldName);
				report.getObjectFields().add(fieldName);
			}

		}

		return report;
	}

	public static List<String> getJavaBeanFieldNames(Class type) {

		// System.out.println("Analysing class " + type.getCanonicalName());

		List<Method> setters = new ArrayList<Method>();
		Map<String, Method> setterNames = new HashMap<String, Method>();

		List<Method> getters = new ArrayList<Method>();
		Map<String, Method> getterNames = new HashMap<String, Method>();

		// get the getters and setters and
		Method[] methods = type.getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if (method.getName().startsWith("set")) {

				// if the function has less or more than one parametere, it is
				// not a JavaBean method / setter
				Class<?>[] parameters = method.getParameterTypes();
				if (parameters.length != 1)
					continue;

				setters.add(method);
				setterNames.put(getFieldName(method.getName()), method);
			} else if (method.getName().startsWith("get")
					|| method.getName().startsWith("is")) {

				// if the function has any parameters at all, its not a JavaBean
				// method / getter
				Class<?>[] parameters = method.getParameterTypes();
				if (parameters.length != 0)
					continue;

				getters.add(method);
				getterNames.put(getFieldName(method.getName()), method);
			}

		}

		// now check the getters and setters with the same fieldName

		List<String> fieldNames = new ArrayList<String>();

		Set<String> names = setterNames.keySet();
		for (String fieldName : names) {
			Method getter = getterNames.get(fieldName);
			if (getter != null) {

				// only store the ones that have declared fields
				Field field = util.getField(type, fieldName);
				if (field == null)
					continue;

				fieldNames.add(fieldName);
				// System.out.println(fieldName);
			}
		}
		return fieldNames;

	}

	public static String getFieldName(String methodName) {
		int index = 0;

		if (methodName.startsWith("set") || methodName.startsWith("get")) {
			String name = methodName.substring(3, 4).toLowerCase()
					+ methodName.substring(4);
			return name;
		} else if (methodName.startsWith("is")) {
			String name = methodName.substring(2, 3).toLowerCase()
					+ methodName.substring(3);
			return name;

		}

		return methodName;

	}
}
